Goals

Comments

suppressMessages(library(cansim))
suppressMessages(library(ggplot2))
suppressMessages(library(dplyr))
suppressMessages(library(plotly))
suppressMessages(library(stringr))
suppressMessages(library(lubridate))
suppressMessages(library(forecast))
library(reshape2)
library(tidyr)
source("../../src/utils.R")

Step 1: Data Retrieval and preprocessing

vector_names <- c(
  "v65201210", "v41690973", "v2062809", "v1001827265",
  "v1001826653", "v52367097", "v4391505", "v800450",
  "v32858858", "v32858865", "v32858872", "v74869", "v129449",
  "v129460", "v129472", "v129482"
)
length(vector_names)
[1] 16
vector_descr <- c(
  "RealGDP", "CPI", "Employment [pers]", "Int. merchendise trade Exp. [$]",
  "Int. merchendise trade Imp. [$]", "Retail Sales [$]", "hours worked",
  "Manufact. sales [$]", "Aircraft domestic [#]", "Aircraft transborder [#]",
  "Aircraft int other [#]", "Railway carloads [tons]", "Travelers US [pers]",
  "Travelers other country [pers]", "CA resident US [pers]",
  "CAresident other country [pers]"
)
vector_descr_diff <- c(
  "RealGDP", "CPI", "Employment", "Int. merchendise trade Exp.",
  "Int. merchendise trade Imp.", "Retail Sales", "hours worked",
  "Manufact. sales", "Aircraft domestic", "Aircraft transborder",
  "Aircraft int other", "Railway carloads", "Travelers US",
  "Travelers other country", "CA resident US",
  "CAresident other country"
)
names(vector_descr) <- vector_names
names(vector_descr_diff) <- vector_names

I expect this date to be before the start of all time series/vectors.

start_date <- "1900-01-01"

Retrieve all vectors. They are concatunated along axis 0.

vectors <- get_cansim_vector(vector_names, start_date)
dim(vectors)
[1] 7506    9

Pivot table to allow each vector in separate column

indicators <- pivot_wider(vectors, names_from = VECTOR, values_from = VALUE)
indicators <- indicators %>%
  group_by(REF_DATE) %>%
  summarise_at(vector_names, sum, na.rm = TRUE)
indicators$REF_YEAR <- format(as.Date(indicators$REF_DATE,
  format = "%Y-%m-%d"
), "%Y")
indicators$REF_MONTH <- format(as.Date(indicators$REF_DATE,
  format = "%Y-%m-%d"
), "%m")
indicators$REF_DAY <- format(as.Date(indicators$REF_DATE,
  format = "%Y-%m-%d"
), "%d")
indicators[indicators == 0] <- NA
head(indicators)

I remove all values before the start of the retail series 1991-01-01.

vectors <- subset(vectors, as.Date(vectors$REF_DATE) >=
  as.Date("1991-01-01"))
indicators <- subset(indicators, as.Date(indicators$REF_DATE) >=
  as.Date("1991-01-01"))

Create a v-vector id to vector desription mapping.

vectors$descr <- sapply(vectors$VECTOR, function(x) {
  vector_descr[x][[1]]
})
# allow fixed order of indicator variables from here on
vectors$descr <- factor(vectors$descr, levels = rev(vector_descr))

Retail sales exhibit strong trend and are not stationary.

p <- ggplot(data = filter(vectors, descr=='Retail Sales [$]'), aes(x = as.Date(REF_DATE), y = VALUE)) +
  geom_line()
p + facet_wrap(~descr, scales = "free_y", ncol = 1, as.table = FALSE)

Transform the time series via first difference to observe if mean and variance are stabilized.

log_first_diff <- function(vector) {
  vector_log <- log(vector)
  vector_log[2:length(vector)] <- vector_log[2:length(vector_log)] -
    vector_log[1:length(vector_log) - 1]
  vector_log[1] <- 0
  vector_log
}
indicators_lfdiff <- data.frame(apply(
  select(indicators, vector_names), 2,
  log_first_diff
))
indicators_lfdiff$REF_DATE <- as.Date(indicators$REF_DATE)
# remove first line/date for which differencing values are not available
indicators_lfdiff <- indicators_lfdiff[-1, ]
vectors_lfdiff <- melt(indicators_lfdiff,
  id = "REF_DATE", na.rm = TRUE,
  variable.name = "VECTOR", value.name = "VALUE"
)
vectors_lfdiff$descr <- sapply(vectors_lfdiff$VECTOR, function(x) {
  vector_descr_diff[x][[1]]
})
# allow fixed order of indicator variables from here on
vectors_lfdiff$descr <- factor(vectors_lfdiff$descr, levels = rev(vector_descr_diff))
filter(vectors_lfdiff, descr=='Retail Sales')
p <- ggplot(data = filter(vectors_lfdiff, descr=='Retail Sales'), aes(x = as.Date(REF_DATE), y = VALUE)) +
  geom_line()
p <- p + facet_wrap(~descr, scales = "free_y", ncol = 1, as.table = FALSE) + labs(y='log diff')
ggplotly(p)

Distribution skew to left due to the Covid-19 crisis.

p <- ggplot(data = filter(vectors_lfdiff, descr=='Retail Sales'), aes(x = VALUE)) +
  geom_histogram()
p + facet_wrap(~descr, scales = "free", ncol = 2, as.table = FALSE)

Check for Stationarity/Variability

Non-stationarity cannot be rejected:

tseries::adf.test(filter(vectors_lfdiff, descr=='Retail Sales')$VALUE)

    Augmented Dickey-Fuller Test

data:  filter(vectors_lfdiff, descr == "Retail Sales")$VALUE
Dickey-Fuller = -0.72491, Lag order = 7, p-value = 0.9678
alternative hypothesis: stationary

If we exclude the outliers due to the crisis we can infer stationarity for I(1)

tseries::adf.test(filter(vectors_lfdiff, descr=='Retail Sales', VALUE>-0.1)$VALUE)
p-value smaller than printed p-value

    Augmented Dickey-Fuller Test

data:  filter(vectors_lfdiff, descr == "Retail Sales", VALUE > -0.1)$VALUE
Dickey-Fuller = -7.4168, Lag order = 7, p-value = 0.01
alternative hypothesis: stationary

Auto-correlations of time series

ACF of the log of the retail sales. Random walk-like pattern?

par(mar=c(5,4,4,2)) 
co2.acf <- acf(log(filter(vectors, descr=='Retail Sales [$]')$VALUE),
  lag.max = 60, na.action = na.pass, ylab = "auto-correlation function",
  main = 'Retail Sales [$]'
)

par(mar=c(5,4,4,2)) 
co2.acf <- pacf(filter(vectors, descr=='Retail Sales [$]')$VALUE,
  lag.max = 12, na.action = na.pass, ylab = "auto-correlation function",
  main = 'Retail Sales [$]'
)

ACF shows lag a slight lag of 1 month. Then drops quickly to zero, indicating that the series is (almost) stationary.

par(mar=c(5,4,4,2)) 
co2.acf <- acf(filter(vectors_lfdiff, descr=='Retail Sales')$VALUE,
  lag.max = 12, na.action = na.pass, ylab = "auto-correlation function",
  main = 'Retail Sales'
)

No lags having high autocrrelation.

par(mar=c(5,4,4,2)) 
co2.acf <- pacf(filter(vectors_lfdiff, descr=='Retail Sales')$VALUE,
  lag.max = 12, na.action = na.pass, ylab = "auto-correlation function",
  main = 'Retail Sales'
)

Correlation between Retail Sales and other short-publication-lagged variables

sample cross-correlation function for which we consider retail sales as predictor variable (see its long publication lag) and hours worked as a dependet variable. I find that when retail sales are high, then 1 month before the hours worked are high. Further correlations beyond 95% CI are not visible.

par(mar=c(5,4,4,2)) 
ccf(filter(vectors_lfdiff, descr=='Retail Sales')$VALUE, filter(vectors_lfdiff, descr=='hours worked')$VALUE,
  lag.max = 12, type = c("correlation"),
  plot = TRUE, na.action = na.pass, ylab = "cross-correlation function",
  main='Retail Sales ~ hours worked'
)

sample cross-correlation function for manufacturing sales and retail sales. Significant lags are not visible within a short horizon.

par(mar=c(5,4,4,2)) 
ccf(filter(vectors_lfdiff, descr=='Retail Sales')$VALUE, filter(vectors_lfdiff, descr=='Manufact. sales')$VALUE, lag.max = 12, type = c("correlation"),
  plot = TRUE, na.action = na.pass, ylab = "cross-correlation function",
  main = 'Retail Sales ~ Manufact. sales'
)

LS0tCnRpdGxlOiAiSUlJLiBJbmRpY2F0b3IgUmV0YWlsIFNhbGVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyMgR29hbHMKCiogTW9yZSBkZXRhaWxlZCBhbmFseXNpcyBvZiAgcmV0YWlsIHNhbGVzIHRpbWUgc2VyaWVzCiogTG9vayBpbnRvIGNvcnJlbGF0aW9uIHdpdGggb3RoZXIgc2VyaWVzCgojIyMgQ29tbWVudHMKCmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoY2Fuc2ltKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGdncGxvdDIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkocGxvdGx5KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHN0cmluZ3IpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkobHVicmlkYXRlKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGZvcmVjYXN0KSkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeSh0aWR5cikKc291cmNlKCIuLi8uLi9zcmMvdXRpbHMuUiIpCmBgYAoKIyMgU3RlcCAxOiBEYXRhIFJldHJpZXZhbCBhbmQgcHJlcHJvY2Vzc2luZwoKYGBge3J9CnZlY3Rvcl9uYW1lcyA8LSBjKAogICJ2NjUyMDEyMTAiLCAidjQxNjkwOTczIiwgInYyMDYyODA5IiwgInYxMDAxODI3MjY1IiwKICAidjEwMDE4MjY2NTMiLCAidjUyMzY3MDk3IiwgInY0MzkxNTA1IiwgInY4MDA0NTAiLAogICJ2MzI4NTg4NTgiLCAidjMyODU4ODY1IiwgInYzMjg1ODg3MiIsICJ2NzQ4NjkiLCAidjEyOTQ0OSIsCiAgInYxMjk0NjAiLCAidjEyOTQ3MiIsICJ2MTI5NDgyIgopCmxlbmd0aCh2ZWN0b3JfbmFtZXMpCmBgYApgYGB7cn0KdmVjdG9yX2Rlc2NyIDwtIGMoCiAgIlJlYWxHRFAiLCAiQ1BJIiwgIkVtcGxveW1lbnQgW3BlcnNdIiwgIkludC4gbWVyY2hlbmRpc2UgdHJhZGUgRXhwLiBbJF0iLAogICJJbnQuIG1lcmNoZW5kaXNlIHRyYWRlIEltcC4gWyRdIiwgIlJldGFpbCBTYWxlcyBbJF0iLCAiaG91cnMgd29ya2VkIiwKICAiTWFudWZhY3QuIHNhbGVzIFskXSIsICJBaXJjcmFmdCBkb21lc3RpYyBbI10iLCAiQWlyY3JhZnQgdHJhbnNib3JkZXIgWyNdIiwKICAiQWlyY3JhZnQgaW50IG90aGVyIFsjXSIsICJSYWlsd2F5IGNhcmxvYWRzIFt0b25zXSIsICJUcmF2ZWxlcnMgVVMgW3BlcnNdIiwKICAiVHJhdmVsZXJzIG90aGVyIGNvdW50cnkgW3BlcnNdIiwgIkNBIHJlc2lkZW50IFVTIFtwZXJzXSIsCiAgIkNBcmVzaWRlbnQgb3RoZXIgY291bnRyeSBbcGVyc10iCikKdmVjdG9yX2Rlc2NyX2RpZmYgPC0gYygKICAiUmVhbEdEUCIsICJDUEkiLCAiRW1wbG95bWVudCIsICJJbnQuIG1lcmNoZW5kaXNlIHRyYWRlIEV4cC4iLAogICJJbnQuIG1lcmNoZW5kaXNlIHRyYWRlIEltcC4iLCAiUmV0YWlsIFNhbGVzIiwgImhvdXJzIHdvcmtlZCIsCiAgIk1hbnVmYWN0LiBzYWxlcyIsICJBaXJjcmFmdCBkb21lc3RpYyIsICJBaXJjcmFmdCB0cmFuc2JvcmRlciIsCiAgIkFpcmNyYWZ0IGludCBvdGhlciIsICJSYWlsd2F5IGNhcmxvYWRzIiwgIlRyYXZlbGVycyBVUyIsCiAgIlRyYXZlbGVycyBvdGhlciBjb3VudHJ5IiwgIkNBIHJlc2lkZW50IFVTIiwKICAiQ0FyZXNpZGVudCBvdGhlciBjb3VudHJ5IgopCm5hbWVzKHZlY3Rvcl9kZXNjcikgPC0gdmVjdG9yX25hbWVzCm5hbWVzKHZlY3Rvcl9kZXNjcl9kaWZmKSA8LSB2ZWN0b3JfbmFtZXMKYGBgCgoKSSBleHBlY3QgdGhpcyBkYXRlIHRvIGJlIGJlZm9yZSB0aGUgc3RhcnQgb2YgYWxsIHRpbWUgc2VyaWVzL3ZlY3RvcnMuCmBgYHtyfQpzdGFydF9kYXRlIDwtICIxOTAwLTAxLTAxIgpgYGAKClJldHJpZXZlIGFsbCB2ZWN0b3JzLiBUaGV5IGFyZSBjb25jYXR1bmF0ZWQgYWxvbmcgYXhpcyAwLgpgYGB7cn0KdmVjdG9ycyA8LSBnZXRfY2Fuc2ltX3ZlY3Rvcih2ZWN0b3JfbmFtZXMsIHN0YXJ0X2RhdGUpCmRpbSh2ZWN0b3JzKQpgYGAKClBpdm90IHRhYmxlIHRvIGFsbG93IGVhY2ggdmVjdG9yIGluIHNlcGFyYXRlIGNvbHVtbgpgYGB7cn0KaW5kaWNhdG9ycyA8LSBwaXZvdF93aWRlcih2ZWN0b3JzLCBuYW1lc19mcm9tID0gVkVDVE9SLCB2YWx1ZXNfZnJvbSA9IFZBTFVFKQppbmRpY2F0b3JzIDwtIGluZGljYXRvcnMgJT4lCiAgZ3JvdXBfYnkoUkVGX0RBVEUpICU+JQogIHN1bW1hcmlzZV9hdCh2ZWN0b3JfbmFtZXMsIHN1bSwgbmEucm0gPSBUUlVFKQoKaW5kaWNhdG9ycyRSRUZfWUVBUiA8LSBmb3JtYXQoYXMuRGF0ZShpbmRpY2F0b3JzJFJFRl9EQVRFLAogIGZvcm1hdCA9ICIlWS0lbS0lZCIKKSwgIiVZIikKaW5kaWNhdG9ycyRSRUZfTU9OVEggPC0gZm9ybWF0KGFzLkRhdGUoaW5kaWNhdG9ycyRSRUZfREFURSwKICBmb3JtYXQgPSAiJVktJW0tJWQiCiksICIlbSIpCmluZGljYXRvcnMkUkVGX0RBWSA8LSBmb3JtYXQoYXMuRGF0ZShpbmRpY2F0b3JzJFJFRl9EQVRFLAogIGZvcm1hdCA9ICIlWS0lbS0lZCIKKSwgIiVkIikKCmluZGljYXRvcnNbaW5kaWNhdG9ycyA9PSAwXSA8LSBOQQpoZWFkKGluZGljYXRvcnMpCmBgYAoKCj4gSSByZW1vdmUgYWxsIHZhbHVlcyBiZWZvcmUgdGhlIHN0YXJ0IG9mIHRoZSByZXRhaWwgc2VyaWVzIDE5OTEtMDEtMDEuCgpgYGB7cn0KdmVjdG9ycyA8LSBzdWJzZXQodmVjdG9ycywgYXMuRGF0ZSh2ZWN0b3JzJFJFRl9EQVRFKSA+PQogIGFzLkRhdGUoIjE5OTEtMDEtMDEiKSkKaW5kaWNhdG9ycyA8LSBzdWJzZXQoaW5kaWNhdG9ycywgYXMuRGF0ZShpbmRpY2F0b3JzJFJFRl9EQVRFKSA+PQogIGFzLkRhdGUoIjE5OTEtMDEtMDEiKSkKYGBgCgpDcmVhdGUgYSB2LXZlY3RvciBpZCB0byB2ZWN0b3IgZGVzcmlwdGlvbiBtYXBwaW5nLgpgYGB7cn0KdmVjdG9ycyRkZXNjciA8LSBzYXBwbHkodmVjdG9ycyRWRUNUT1IsIGZ1bmN0aW9uKHgpIHsKICB2ZWN0b3JfZGVzY3JbeF1bWzFdXQp9KQojIGFsbG93IGZpeGVkIG9yZGVyIG9mIGluZGljYXRvciB2YXJpYWJsZXMgZnJvbSBoZXJlIG9uCnZlY3RvcnMkZGVzY3IgPC0gZmFjdG9yKHZlY3RvcnMkZGVzY3IsIGxldmVscyA9IHJldih2ZWN0b3JfZGVzY3IpKQpgYGAKClJldGFpbCBzYWxlcyBleGhpYml0IHN0cm9uZyB0cmVuZCBhbmQgYXJlIG5vdCBzdGF0aW9uYXJ5LgpgYGB7cn0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGZpbHRlcih2ZWN0b3JzLCBkZXNjcj09J1JldGFpbCBTYWxlcyBbJF0nKSwgYWVzKHggPSBhcy5EYXRlKFJFRl9EQVRFKSwgeSA9IFZBTFVFKSkgKwogIGdlb21fbGluZSgpCnAgKyBmYWNldF93cmFwKH5kZXNjciwgc2NhbGVzID0gImZyZWVfeSIsIG5jb2wgPSAxLCBhcy50YWJsZSA9IEZBTFNFKQpgYGAKCgpUcmFuc2Zvcm0gdGhlIHRpbWUgc2VyaWVzIHZpYSBmaXJzdCBkaWZmZXJlbmNlIHRvIG9ic2VydmUgaWYgbWVhbiBhbmQgdmFyaWFuY2UgYXJlIHN0YWJpbGl6ZWQuCgpgYGB7cn0KbG9nX2ZpcnN0X2RpZmYgPC0gZnVuY3Rpb24odmVjdG9yKSB7CiAgdmVjdG9yX2xvZyA8LSBsb2codmVjdG9yKQogIHZlY3Rvcl9sb2dbMjpsZW5ndGgodmVjdG9yKV0gPC0gdmVjdG9yX2xvZ1syOmxlbmd0aCh2ZWN0b3JfbG9nKV0gLQogICAgdmVjdG9yX2xvZ1sxOmxlbmd0aCh2ZWN0b3JfbG9nKSAtIDFdCiAgdmVjdG9yX2xvZ1sxXSA8LSAwCiAgdmVjdG9yX2xvZwp9CmBgYAoKCmBgYHtyfQppbmRpY2F0b3JzX2xmZGlmZiA8LSBkYXRhLmZyYW1lKGFwcGx5KAogIHNlbGVjdChpbmRpY2F0b3JzLCB2ZWN0b3JfbmFtZXMpLCAyLAogIGxvZ19maXJzdF9kaWZmCikpCmluZGljYXRvcnNfbGZkaWZmJFJFRl9EQVRFIDwtIGFzLkRhdGUoaW5kaWNhdG9ycyRSRUZfREFURSkKIyByZW1vdmUgZmlyc3QgbGluZS9kYXRlIGZvciB3aGljaCBkaWZmZXJlbmNpbmcgdmFsdWVzIGFyZSBub3QgYXZhaWxhYmxlCmluZGljYXRvcnNfbGZkaWZmIDwtIGluZGljYXRvcnNfbGZkaWZmWy0xLCBdCgp2ZWN0b3JzX2xmZGlmZiA8LSBtZWx0KGluZGljYXRvcnNfbGZkaWZmLAogIGlkID0gIlJFRl9EQVRFIiwgbmEucm0gPSBUUlVFLAogIHZhcmlhYmxlLm5hbWUgPSAiVkVDVE9SIiwgdmFsdWUubmFtZSA9ICJWQUxVRSIKKQp2ZWN0b3JzX2xmZGlmZiRkZXNjciA8LSBzYXBwbHkodmVjdG9yc19sZmRpZmYkVkVDVE9SLCBmdW5jdGlvbih4KSB7CiAgdmVjdG9yX2Rlc2NyX2RpZmZbeF1bWzFdXQp9KQojIGFsbG93IGZpeGVkIG9yZGVyIG9mIGluZGljYXRvciB2YXJpYWJsZXMgZnJvbSBoZXJlIG9uCnZlY3RvcnNfbGZkaWZmJGRlc2NyIDwtIGZhY3Rvcih2ZWN0b3JzX2xmZGlmZiRkZXNjciwgbGV2ZWxzID0gcmV2KHZlY3Rvcl9kZXNjcl9kaWZmKSkKYGBgCgpgYGB7cn0KZmlsdGVyKHZlY3RvcnNfbGZkaWZmLCBkZXNjcj09J1JldGFpbCBTYWxlcycpCmBgYAoKCmBgYHtyfQpwIDwtIGdncGxvdChkYXRhID0gZmlsdGVyKHZlY3RvcnNfbGZkaWZmLCBkZXNjcj09J1JldGFpbCBTYWxlcycpLCBhZXMoeCA9IGFzLkRhdGUoUkVGX0RBVEUpLCB5ID0gVkFMVUUpKSArCiAgZ2VvbV9saW5lKCkKcCA8LSBwICsgZmFjZXRfd3JhcCh+ZGVzY3IsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMSwgYXMudGFibGUgPSBGQUxTRSkgKyBsYWJzKHk9J2xvZyBkaWZmJykKZ2dwbG90bHkocCkKYGBgCgpEaXN0cmlidXRpb24gc2tldyB0byBsZWZ0IGR1ZSB0byB0aGUgQ292aWQtMTkgY3Jpc2lzLgpgYGB7cn0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGZpbHRlcih2ZWN0b3JzX2xmZGlmZiwgZGVzY3I9PSdSZXRhaWwgU2FsZXMnKSwgYWVzKHggPSBWQUxVRSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCnAgKyBmYWNldF93cmFwKH5kZXNjciwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gMiwgYXMudGFibGUgPSBGQUxTRSkKYGBgCgojIyMgQ2hlY2sgZm9yIFN0YXRpb25hcml0eS9WYXJpYWJpbGl0eQoKTm9uLXN0YXRpb25hcml0eSBjYW5ub3QgYmUgcmVqZWN0ZWQ6CmBgYHtyfQp0c2VyaWVzOjphZGYudGVzdChmaWx0ZXIodmVjdG9yc19sZmRpZmYsIGRlc2NyPT0nUmV0YWlsIFNhbGVzJykkVkFMVUUpCmBgYAoKSWYgd2UgZXhjbHVkZSB0aGUgb3V0bGllcnMgZHVlIHRvIHRoZSBjcmlzaXMgd2UgY2FuIGluZmVyIHN0YXRpb25hcml0eSBmb3IgSSgxKQpgYGB7cn0KdHNlcmllczo6YWRmLnRlc3QoZmlsdGVyKHZlY3RvcnNfbGZkaWZmLCBkZXNjcj09J1JldGFpbCBTYWxlcycsIAogICAgICAgICAgICAgICAgICAgICAgICAgVkFMVUU+LTAuMSkkVkFMVUUpCmBgYAoKCiMjIyBBdXRvLWNvcnJlbGF0aW9ucyBvZiB0aW1lIHNlcmllcwoKQUNGICBvZiB0aGUgbG9nIG9mIHRoZSByZXRhaWwgc2FsZXMuIFJhbmRvbSB3YWxrLWxpa2UgcGF0dGVybj8KYGBge3IsIGZpZy5oZWlnaHQ9NH0KcGFyKG1hcj1jKDUsNCw0LDIpKSAKY28yLmFjZiA8LSBhY2YobG9nKGZpbHRlcih2ZWN0b3JzLCBkZXNjcj09J1JldGFpbCBTYWxlcyBbJF0nKSRWQUxVRSksCiAgbGFnLm1heCA9IDYwLCBuYS5hY3Rpb24gPSBuYS5wYXNzLCB5bGFiID0gImF1dG8tY29ycmVsYXRpb24gZnVuY3Rpb24iLAogIG1haW4gPSAnUmV0YWlsIFNhbGVzIFskXScKKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnBhcihtYXI9Yyg1LDQsNCwyKSkgCmNvMi5hY2YgPC0gcGFjZihmaWx0ZXIodmVjdG9ycywgZGVzY3I9PSdSZXRhaWwgU2FsZXMgWyRdJykkVkFMVUUsCiAgbGFnLm1heCA9IDEyLCBuYS5hY3Rpb24gPSBuYS5wYXNzLCB5bGFiID0gImF1dG8tY29ycmVsYXRpb24gZnVuY3Rpb24iLAogIG1haW4gPSAnUmV0YWlsIFNhbGVzIFskXScKKQpgYGAKCgpBQ0Ygc2hvd3MgbGFnIGEgc2xpZ2h0IGxhZyBvZiAxIG1vbnRoLiBUaGVuIGRyb3BzIHF1aWNrbHkgdG8gemVybywgCmluZGljYXRpbmcgdGhhdCB0aGUgc2VyaWVzIGlzIChhbG1vc3QpIHN0YXRpb25hcnkuCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnBhcihtYXI9Yyg1LDQsNCwyKSkgCmNvMi5hY2YgPC0gYWNmKGZpbHRlcih2ZWN0b3JzX2xmZGlmZiwgZGVzY3I9PSdSZXRhaWwgU2FsZXMnKSRWQUxVRSwKICBsYWcubWF4ID0gMTIsIG5hLmFjdGlvbiA9IG5hLnBhc3MsIHlsYWIgPSAiYXV0by1jb3JyZWxhdGlvbiBmdW5jdGlvbiIsCiAgbWFpbiA9ICdSZXRhaWwgU2FsZXMnCikKYGBgCgpObyBsYWdzIGhhdmluZyBoaWdoIGF1dG9jcnJlbGF0aW9uLgpgYGB7ciwgZmlnLmhlaWdodD00fQpwYXIobWFyPWMoNSw0LDQsMikpIApjbzIuYWNmIDwtIHBhY2YoZmlsdGVyKHZlY3RvcnNfbGZkaWZmLCBkZXNjcj09J1JldGFpbCBTYWxlcycpJFZBTFVFLAogIGxhZy5tYXggPSAxMiwgbmEuYWN0aW9uID0gbmEucGFzcywgeWxhYiA9ICJhdXRvLWNvcnJlbGF0aW9uIGZ1bmN0aW9uIiwKICBtYWluID0gJ1JldGFpbCBTYWxlcycKKQpgYGAKCgojIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIFJldGFpbCBTYWxlcyBhbmQgb3RoZXIgc2hvcnQtcHVibGljYXRpb24tbGFnZ2VkIHZhcmlhYmxlcwoKCnNhbXBsZSBjcm9zcy1jb3JyZWxhdGlvbiBmdW5jdGlvbiBmb3Igd2hpY2ggd2UgY29uc2lkZXIgcmV0YWlsIHNhbGVzIGFzIHByZWRpY3RvciB2YXJpYWJsZSAoc2VlIGl0cyBsb25nIHB1YmxpY2F0aW9uIGxhZykgYW5kIGhvdXJzIHdvcmtlZCBhcyBhIGRlcGVuZGV0IHZhcmlhYmxlLiBJIGZpbmQgdGhhdCB3aGVuIHJldGFpbCBzYWxlcyBhcmUgaGlnaCwgdGhlbiAxIG1vbnRoIGJlZm9yZSB0aGUgaG91cnMgd29ya2VkIGFyZSBoaWdoLiBGdXJ0aGVyIGNvcnJlbGF0aW9ucyBiZXlvbmQgOTUlIENJIGFyZSBub3QgdmlzaWJsZS4KYGBge3IsIGZpZy5oZWlnaHQ9NH0KcGFyKG1hcj1jKDUsNCw0LDIpKSAKY2NmKGZpbHRlcih2ZWN0b3JzX2xmZGlmZiwgZGVzY3I9PSdSZXRhaWwgU2FsZXMnKSRWQUxVRSwgZmlsdGVyKHZlY3RvcnNfbGZkaWZmLCBkZXNjcj09J2hvdXJzIHdvcmtlZCcpJFZBTFVFLAogIGxhZy5tYXggPSAxMiwgdHlwZSA9IGMoImNvcnJlbGF0aW9uIiksCiAgcGxvdCA9IFRSVUUsIG5hLmFjdGlvbiA9IG5hLnBhc3MsIHlsYWIgPSAiY3Jvc3MtY29ycmVsYXRpb24gZnVuY3Rpb24iLAogIG1haW49J1JldGFpbCBTYWxlcyB+IGhvdXJzIHdvcmtlZCcKKQpgYGAKCnNhbXBsZSBjcm9zcy1jb3JyZWxhdGlvbiBmdW5jdGlvbiBmb3IgbWFudWZhY3R1cmluZyBzYWxlcyBhbmQgcmV0YWlsIHNhbGVzLgpTaWduaWZpY2FudCBsYWdzIGFyZSBub3QgdmlzaWJsZSB3aXRoaW4gYSBzaG9ydCBob3Jpem9uLgpgYGB7ciwgZmlnLmhlaWdodD00fQpwYXIobWFyPWMoNSw0LDQsMikpIApjY2YoZmlsdGVyKHZlY3RvcnNfbGZkaWZmLCBkZXNjcj09J1JldGFpbCBTYWxlcycpJFZBTFVFLCBmaWx0ZXIodmVjdG9yc19sZmRpZmYsIGRlc2NyPT0nTWFudWZhY3QuIHNhbGVzJykkVkFMVUUsIGxhZy5tYXggPSAxMiwgdHlwZSA9IGMoImNvcnJlbGF0aW9uIiksCiAgcGxvdCA9IFRSVUUsIG5hLmFjdGlvbiA9IG5hLnBhc3MsIHlsYWIgPSAiY3Jvc3MtY29ycmVsYXRpb24gZnVuY3Rpb24iLAogIG1haW4gPSAnUmV0YWlsIFNhbGVzIH4gTWFudWZhY3QuIHNhbGVzJwopCmBgYAoKCgo=